Skip to content

chore: main's own checks green again (sorted compile env list and its audit, file-size counts, GC header registry, held/named length unit tests) - #14526

Merged
proggeramlug merged 9 commits into
mainfrom
chore/main-hygiene
Nov 12, 2026
Merged

proggeramlug merged 9 commits into
mainfrom
chore/main-hygiene

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

What

Main's own checks were red in several places that no lane PR owned. This PR makes them green again. No lowering, fact or runtime code changes.

1. COMPILE_ENV_VARS is sorted again, and the audit behind it runs

The list is documented "sorted and unique", and compile_env::tests::codegen_env_vars_are_build_cache_inputs asserts it, but lane switches had been appended by topic (20 adjacent pairs out of order). The assert sits in front of the unregistered-read check, so that check had not run for days.

  • The list is sorted (byte order, as the test's < compares) and unique: 302 names.

  • With the sort in place the audit reported 11 compiler env reads that were neither keyed nor excluded. Five change emitted code and are now cache inputs:

    • PERRY_FACTS_DCE (dead bodies become traps),
    • PERRY_INDEX_RAW_I32 (the raw $i32 body of indexed clones),
    • PERRY_SNAP_CENSUS (census compiles declare and link the TLS anchors and entry hooks),
    • PERRY_WP_ELEMENT_READS (withholds the proven element reads),
    • PERRY_WP_REP_P2 (typed parameters).

    Before this, a build with one of them set could reuse an object or a finished build made without it.

  • Six only print or dump and are excluded with a one-line reason each: PERRY_ARRAY_BASE_DIAG, PERRY_FACTS_ZERO_SIGN_WHY, PERRY_REST_DIAG, PERRY_SNAP_FACTS, PERRY_TEST_IR_OUT (read only by perry-codegen unit tests), PERRY_VALUE_RECORDS_TRACE.

Cache keys. Both consumers walk the list in order: object_cache::compute_object_cache_key hashes name=value per entry, and build_cache::current_env stores a Vec that is compared with !=. So the order is part of both keys: every existing object-cache entry and build-cache manifest misses once after this PR and is rebuilt. Nothing depends on a position in the list, so nothing else changes.

2. scripts/check_file_size.sh: six stale allowlist counts re-recorded

--update semantics; what grew each file since its count was last set:

file asserted now grown by
perry-codegen/src/codegen/mod.rs 3673 3677 life census (d4c2d84), Buffer read folds #14511
perry-codegen/src/expr/mod.rs 2876 2875 #14511
perry-codegen/tests/native_proof_regressions.rs 15687 15688 Buffer.equals folds (d0ac888, 6825563), deferred imports #14421
perry-runtime/src/gc/policy.rs 2860 2877 leaf-only collection (e239eb9, 119d27e), snap census 7b9b809
perry-runtime/src/object/shapes.rs 4928 4937 snap census 7b9b809
perry-runtime/src/snap_census/runner.rs 3576 4679 snap census (0521f89, 89fd729, 7b9b809): +1103 lines

runner.rs grew by 1103 lines in three commits. Its entry already says "split after #14259 lands (snap census owner)"; this PR only re-records the count. gen_expr.rs (over the cap, not allowlisted) was split in #14525.

3. scripts/check_gc_header_constants.py: the registry follows the mask split of 39359ae

The script reported GC_OBJECT_METHOD_GUARD_MASK_I32 = 0x80FF against a derivation that includes OBJ_FLAG_HAS_DESCRIPTORS (0x080080FF), and GC_OBJECT_FIELD_GUARD_MASK_I32 as unregistered. The compiler is right and the script was stale:

  • 39359ae (2026-11-04, "method guards stop reading the descriptor bit") split the one mask in two on purpose. A method-identity guard needs no attribute bit: an own key named like the method is in the key list, so the class/ShapeId word or the family word already misses. Field-layout guards (raw slot loads and stores) keep the bit under the new GC_OBJECT_FIELD_GUARD_MASK_I32. The commit changed codegen, the runtime and the unit test gc_header_mask_preserves_the_direct_method_contract (which pins both values and asserts that the method mask ignores the bit and the field mask does not), but not this script's registry.
  • Runtime side unchanged: GC_FLAG_FORWARDED = 0x80 (byte 1), OBJ_FLAG_HAS_DESCRIPTORS = 0x800 (the reserved half), so 0x80FF and 0x080080FF are exactly the two documented masks.
  • Probe (qb10, main 3cd7ef9): a 36-line program that calls a class method through a warmed direct-method site and then, on the same receiver, defines an own accessor / non-writable data property of the method's name, a descriptor on another key, freezes / seals / prevents extensions, assigns and deletes a shadow, defines the shadow in the constructor, and redefines the prototype's method as an accessor and as non-writable data. Its IR has 28 guards with the 0x80FF mask; output equals node in default and check mode.

The registry now holds both masks with their derivations; the check and its self-test pass.

4. Negative unit tests that passed for the wrong reason

perry-wpfacts tests/held_lengths.rs: the three negative cases (handed, closed, Holder.walk) bore their Arrays at one site (mk), and a token is its birth site. The closure of one case and the field of another therefore refused that token for all three (probe on main: each of them, and grow's parameter, prints refused=captured:local+…+heap:field:tok24), so handed never reached the hand-over rule and each test would have passed with its own construct removed. The fact is also asked only of Rep::Arr locals, which no test asserted. Now every case bears its Arrays at its own site, and

  • a positive case asserts Rep::Arr and the fact;
  • a negative case asserts Rep::Arr, the missing fact and the rule that refused it, read off the # held_len … refused= dump line: event:own (the function pushes), passed:own (handed to a call), captured (a closure), heap (a field);
  • each negative case has a positive twin that differs only in the refused construct (grow(xs) / touch(xs.length), a closure over xs / over a module let, this.items / a frame local, a.push(1) / a.length).

tests/named_lengths.rs (from #14521) gets the same treatment: every case asserts Rep::Arr first, the three negative cases assert refused=event:other-name(, and picked and Bag.walk get twins that change the length through the local's own name.

5. Two more red gates with a mechanical fix

  • scripts/runtime_abi_check.py --check-wasm-abi: crates/perry-codegen/src/wasm32/runtime_abi.tsv was stale; regenerated with --emit-wasm-abi (16 new runtime exports: record arrays, deferred namespaces, snap census sites, setenv/unsetenv/gettimeofday; one changed signature, js_snap_entry_module). The table feeds the wasm32 target only.
  • scripts/unrooted_local_shape.py --check: stale baseline (a file reached zero findings); --update-baseline ratchets it down, 208 -> 178.

Other static gates that are red on main and are not touched here are listed in the comment below.

Switch: none (no lowering change; the five registrations are existing switches becoming cache inputs).

Checks

Head 3c83e9f on main 3cd7ef9.

  • qb10 cargo test --release -p perry compile_env: 33 passed, 0 failed (on main: codegen_env_vars_are_build_cache_inputs fails at the sort assert; with only the sort applied it fails with the 11 names above).
  • qb10 cargo test --release -p perry-wpfacts --lib -- held_lengths named_lengths: 16 passed, 0 failed.
  • qb10 scripts/check_file_size.sh: rc 0. scripts/check_gc_header_constants.py and its --self-test: rc 0. runtime_abi_check.py --check-wasm-abi, unrooted_local_shape.py --check: OK.
  • Mac cargo check with tests: -p perry --tests, -p perry-wpfacts --lib --tests, -p perry-codegen --lib --tests: clean.
  • Chess, base (main 3cd7ef9) vs this PR, one release build each on qb10: in the comment below.

Long gates (IR census, vf, tssrc, Supabase, skin size, full suites): not run.

@proggeramlug
proggeramlug merged commit 403387f into main Nov 12, 2026
94 of 101 checks passed
@proggeramlug
proggeramlug deleted the chore/main-hygiene branch November 12, 2026 06:16
@coderabbitai

coderabbitai Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Explain Change →

Caution

Review failed

The pull request is closed.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@proggeramlug

Copy link
Copy Markdown
Contributor Author

Chess, base (main 3cd7ef9) vs this PR (3c83e9f = that main + this PR), one release build each on qb10, each compiled and run twice, perf stat -e instructions:u, M:

search 250000 perft 4 evaluate 100000 legal 10000
base 11 858.7, 11 858.7 258.9, 258.9 1 191.8, 1 191.8 354.8, 354.8
PR 11 858.6, 11 858.7 258.9, 258.9 1 191.8, 1 191.8 354.8, 354.8

The chess binary is byte-identical in both arms (sha256 4075423512de…, all four compiles), checksums and PV equal, peak RSS 26.0-26.5 MB search and 18.7-19.2 MB the others in both arms. The PR head as merged also contains main d15e776 (#14523), which was not rebuilt.

perry-wpfacts lib tests of the group #14525 moved (async await promise iterat for_of generator): 123 passed, 0 failed, on this PR's head.

Static gates of the CI lint job that are red on main 3cd7ef9 and that this PR does not touch (run on qb10 and on a Mac, 95 commands; each needs a decision by the owner of the code it names, not a re-record):

gate what it reports
gc_store_site_inventory.py 7 raw slot writes without a GC_STORE_AUDIT marker (async_module.rs:55, module_require/init_guard_tests.rs:22-23, record_array.rs:213,239, typedarray/slice_ops.rs:258,260); stale IR witness registry entry class_field_set
gc_root_dominance_check.py --audit-poll-reach js_object_assign_fresh calls the poll-capable js_object_assign_one but is not in POLL_CAPABLE_RUNTIME
gc_runtime_root_holders.py (+ --self-test) 3 unclassified holders (external_family.rs:136 INFO, node_sea.rs:8 SEA_BLOB_FACTORY, class_registry/construct.rs:1121 METHOD_CALLS), 11 stale inventory entries, gc/census.rs PASS1_MARKED pins stale
addr_class_inventory.py layout_slot.rs:85 hand-rolled handle floor (ratchet 0), gcheader-cast at array/fill_birth.rs:132 and layout_slot.rs:88
raw_handle_debt.py new debt sites without a ceiling (typedarray/{iterate,slice_ops}.rs, typedarray_named.rs, typedarray_props.rs, validators/received.rs, value/dynamic_object.rs), ceilings exceeded (typedarray/transform.rs 14 > 2, value/dyn_index.rs 5 > 3, weakref.rs 10 > 4), 11 stale ceilings to delete
native_handle_ledger.py fs/stream/write_file_input/deferred.rs 1 table over a ceiling of 0; 2 stale entries (perry-ext-http turnloop_h2/mod.rs, turnloop_serve/conn.rs)
thread_exit_address_globals.py 27 unclassified process-global statics (CatchStack keys, snap_census/runner.rs, string/short_pair.rs, turnloop counters)
sso_unbox_inventory.py proof sources changed (string/concat.rs, concat_parts.rs, array/join.rs, property_ops/put_value.rs, ...): reviews to redo
string_payload_access_inventory.py open-coded StringHeader payload offsets (url/url_class.rs, url/search_params.rs, update_notify.rs, ...)
reference_test_inventory.py value/dyn_index.rs js_dyn_index_set_strict: 9 raw pointer-tag tests, 8 justified
local_binding_type_audit.py unclassified local-type read proto_constants.rs::iterator_member_site line 1038
shape_descriptor_census.py perry_probe_get bypasses the receiver-layout authority
check_test_registration.py (+ --self-test) 2 parity tests not in tests/parity/gc_repsel_corpus.txt (node-suite/worker_threads/test_gap_gc_namespace_call_callee_rooting.ts, language/class/test_gap_gc_scalar_ctor_arg_rooting.ts)
gc_repsel_matrix.sh --self-test-fixture-env routing self-test: the wb_off cell's compile env is not {PERRY_WRITE_BARRIERS: 0}
check_gc_doc_claims.py AGENTS.md names two placeholder paths (tests/parity/<root>/<subject>/, tests/parity/snapshots/<platform>-<mode>/<subject>.json) the checker reads as real paths (since #14117)

check_object_header_access.sh --self-test fails on macOS only (BSD grep has no -P); it passes on Linux.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant